package com.baijiayun.live.ui.toolbox.redpacket.widget;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.HandlerThread;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.baijiayun.live.ui.R;
import com.baijiayun.livecore.models.LPRedPacketModel;
import com.baijiayun.livecore.utils.DisplayUtils;
import com.baijiayun.livecore.utils.LPRxUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;

public class TransparentSurfaceView extends SurfaceView implements DrawInterface {

    private final String TAG = TransparentSurfaceView.class.getName();

    private final int MAX_RED_PACKET_NUMBER = 10;//红包最多个数
    private final int OPEN_SHOW_TIME = 500;//点开显示时间
    private final int COUNT_OPEN_BITMAP = 14;//打开效果图序列
    private final int OPEN_SHOW_OPEN_TIME = 50;//打开序列单个显示时间
    private final float RED_POCKET_DROP_TIME = 3.0f;

    private OnClickRedPacketListener onClickRedPacketListener;

    private DrawHandler drawHandler;

    private int width;
    private int height;

    private Bitmap bitmap;
    private Bitmap[] bitmapOpen;

    private int bitmapWidth;
    private int bitmapHeight;
    private int bitmapWidthOpen;
    private int bitmapHeightOpen;

    private Paint mPaintTitle;
    private Paint mPaintContext;
    //红包雨状态是否可以抢
    private boolean isRobEnable = false;
    //红包集合
    private List<MoveModel> moveList = new CopyOnWriteArrayList<>();
    private List<RedPointF> pointFList = new CopyOnWriteArrayList<>();
    private Paint bitmapPaint, strokePaint, fillPaint, debugPaint;
    private Path drawingPath;
    private boolean isPause;
    private Disposable disposableOfInterval;

    public TransparentSurfaceView(Context context) {
        this(context, null);
        init(context);
    }

    public TransparentSurfaceView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
        init(context);
    }

    public TransparentSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        SurfaceHolder holder = getHolder();
        setZOrderOnTop(true);
        holder.setFormat(PixelFormat.TRANSLUCENT);

        mPaintTitle = new Paint();
        mPaintTitle.setAntiAlias(true);
        mPaintTitle.setColor(Color.WHITE);
        mPaintTitle.setTextSize(DisplayUtils.dip2px(context, 10));

        mPaintContext = new Paint();
        mPaintContext.setAntiAlias(true);
        mPaintContext.setColor(Color.parseColor("#FFDBDB"));
        mPaintContext.setTextSize(DisplayUtils.dip2px(context, 14));

        HandlerThread drawThread = new HandlerThread("redPacket");
        drawThread.start();

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;

        drawHandler = new DrawHandler(drawThread.getLooper(), this);
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_packet_down, options);
        bitmapWidth = DisplayUtils.dip2px(getContext(), 59);
        bitmapHeight = DisplayUtils.dip2px(getContext(), 118);

        bitmapOpen = new Bitmap[COUNT_OPEN_BITMAP];
        bitmapOpen[0] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_1, options);
        bitmapOpen[1] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_2, options);
        bitmapOpen[2] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_3, options);
        bitmapOpen[3] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_4, options);
        bitmapOpen[4] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_5, options);
        bitmapOpen[5] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_6, options);
        bitmapOpen[6] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_7, options);
        bitmapOpen[7] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_8, options);
        bitmapOpen[8] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_9, options);
        bitmapOpen[9] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_10, options);
        bitmapOpen[10] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_11, options);
        bitmapOpen[11] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_12, options);
        bitmapOpen[12] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_13, options);
        bitmapOpen[13] = BitmapFactory.decodeResource(getResources(), R.drawable.iv_lp_ui_red_open_14, options);

        bitmapWidthOpen = DisplayUtils.dip2px(getContext(), 120);
        bitmapHeightOpen = bitmapWidthOpen;

        bitmapPaint = new Paint();
        bitmapPaint.setAntiAlias(true);
        strokePaint = new Paint();
        strokePaint.setAntiAlias(true);
        strokePaint.setColor(Color.parseColor("#F7B500"));
        strokePaint.setStrokeWidth(3);
        strokePaint.setStyle(Paint.Style.STROKE);
        fillPaint = new Paint();
        fillPaint.setAntiAlias(true);
        fillPaint.setColor(Color.WHITE);
        fillPaint.setStyle(Paint.Style.FILL);
        debugPaint = new Paint();
        debugPaint.setAntiAlias(true);
        debugPaint.setColor(Color.RED);
        debugPaint.setTextSize(40f);
        drawingPath = new Path();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth();
        height = getMeasuredHeight();
    }

    public void start(LPRedPacketModel redPacketModel) {
        isPause = false;
        moveList.clear();
        totalDropCount = 0;
        disposableOfInterval = Observable.interval(0, (long) RED_POCKET_DROP_TIME, TimeUnit.SECONDS)
                .filter(aLong -> aLong < (redPacketModel.duration / RED_POCKET_DROP_TIME))
                .observeOn(AndroidSchedulers.mainThread()).subscribe(aLong -> {
                    for (int i = 0; i < MAX_RED_PACKET_NUMBER; i++) {
                        generateModel();
                    }
                });
        drawHandler.sendEmptyMessage(DrawHandler.START_DRAW_KEY);
    }

    public void pause() {
        if(!isPause){
            drawHandler.sendEmptyMessage(DrawHandler.STOP_DRAW_KEY);
        }
        isPause = true;
        LPRxUtils.dispose(disposableOfInterval);
    }

    public void setOnClickRedPacketListener(OnClickRedPacketListener listener) {
        onClickRedPacketListener = listener;
    }
    //当前屏幕总数
    private int visibleCountOfScreen;
    private int totalCountOfScreen;
    //总掉落红包数
    private int totalDropCount;
    private RectF bitmapRect = new RectF();
    private RectF coinRect = new RectF();
    private int redPacketId = 1;


    @Override
    public void startDraw() {
        SurfaceHolder holder = getHolder();
        Canvas canvas = holder.lockCanvas();
        if (null == canvas) {
            //继续发送事件不让循环在后台断掉
            drawHandler.sendEmptyMessage(DrawHandler.START_DRAW_KEY);
            return;
        }
        long timestamp = System.currentTimeMillis();
        totalCountOfScreen = visibleCountOfScreen = 0;
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        for (MoveModel moveModel : moveList) {
            bitmapRect.set(moveModel.x, moveModel.y, moveModel.x + bitmapWidth, moveModel.y + bitmapHeight);
            //被抢中后继续掉落只是不显示
            if(moveModel.y >= -bitmapHeight){
                if(!moveModel.isOpen){
                    if(moveModel.firstShowTime == 0){
                        moveModel.firstShowTime = System.currentTimeMillis();
                    }
                    canvas.drawBitmap(bitmap, null, bitmapRect, bitmapPaint);
                    visibleCountOfScreen++;
                }
                totalCountOfScreen++;
            }
            if (moveModel.y >= height) {
                totalDropCount++;
                moveList.remove(moveModel);
            } else {
                float second = ((timestamp - moveModel.createTime) / 1000.f);
                float pixelPerSecond = (height + bitmapHeight) / (RED_POCKET_DROP_TIME * 1.0f);
                moveModel.y = pixelPerSecond * second + moveModel.initY;
            }
            if(!moveModel.isOpen){
                continue;
            }
            //保存打开红包时y轴坐标值
            if(moveModel.openedY == 0){
                moveModel.openedY = moveModel.y;
            }
            //没抢中或者已经显示过500ms的金币动画直接跳过
            if(moveModel.hasShowCoinAnim || moveModel.scoreAmount <= 0){
                moveModel.hasShowCoinAnim = true;
                continue;
            }
            //显示金币动画
            if(moveModel.openTime == 0){
                moveModel.openTime = timestamp;
            }
            if ((timestamp - moveModel.openTime) < OPEN_SHOW_TIME) {
                float x = (moveModel.x + (bitmapWidth >> 1)) - (bitmapWidthOpen >> 1);
                float y = (moveModel.openedY + (bitmapHeight >> 1)) - (bitmapHeightOpen >> 1);

                int index = (int) ((timestamp - moveModel.openTime) / OPEN_SHOW_OPEN_TIME);
                if (index > COUNT_OPEN_BITMAP - 1)
                    index = COUNT_OPEN_BITMAP - 1;
                if (index < 0 || index >= bitmapOpen.length) {
                    continue;
                }
                coinRect.set(x, y, x + bitmapWidthOpen, y + bitmapHeightOpen);
                canvas.drawBitmap(bitmapOpen[index], null, coinRect, bitmapPaint);
                int moveY = (int) (y + bitmapHeightOpen / 3 - (index * (bitmapHeightOpen / 3 * 2) / COUNT_OPEN_BITMAP));
                canvas.drawText("+" + moveModel.scoreAmount, x + (coinRect.right - coinRect.left) / 8 * 3, moveY, mPaintContext);
            } else {
                moveModel.hasShowCoinAnim = true;
            }
        }
        computePath(canvas, pointFList, 8.0f);
        holder.unlockCanvasAndPost(canvas);
        drawHandler.sendEmptyMessage(DrawHandler.START_DRAW_KEY);
    }

    @Override
    public void stopDraw() {
        drawHandler.removeMessages(DrawHandler.START_DRAW_KEY);
        totalDropCount += totalCountOfScreen;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isRobEnable || isPause) {
            return true;
        }
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                pointFList.add(new RedPointF(event.getX(), event.getY()));
                checkInRect();
                break;
            case MotionEvent.ACTION_MOVE:
                int index = 0;
                for (int i = 0; i < pointFList.size(); i++) {
                    if (System.currentTimeMillis() - pointFList.get(i).timeStamp <= 200) {
                        index = i;
                        break;
                    }
                }
                pointFList.removeAll(pointFList.subList(0, index));
                pointFList.add(new RedPointF(event.getX(), event.getY()));
                checkInRect();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                pointFList.clear();
                break;
            default:
                break;
        }
        return true;
    }

    private void computePath(Canvas canvas, List<RedPointF> points, float lineWidth) {
        RedPointF[] pointArray = points.toArray(new RedPointF[0]);
        if (pointArray == null || pointArray.length <= 2) {
            return;
        }
        List<PointF> upPoints = new ArrayList<>();
        List<PointF> downPoints = new ArrayList<>();
        float x0 = pointArray[pointArray.length - 1].x;
        float y0 = pointArray[pointArray.length - 1].y;
        for (int i = pointArray.length - 2; i > 0; i--) {
            float x1 = pointArray[i].x;
            float y1 = pointArray[i].y;
            float x2 = pointArray[i-1].x;
            float y2 = pointArray[i-1].y;

            // 起点法向量模
            float startLen = (float) Math.sqrt((y2 - y0) * (y2 - y0) + (x0 - x2) * (x0 - x2));
            // 折点法向量模
            float infLen = (float) Math.sqrt((y1 - y0) * (y1 - y0) + (x0 - x1) * (x0 - x1));
            // 起点单位法向量
            float startX = (y2 - y0) / startLen;
            float startY = (x0 - x2) / startLen;
            // 折点单位法向量
            float infX = (y1 - y0) / infLen;
            float infY = (x0 - x1) / infLen;

            // 折点单位法向量与起点单位法向量的夹角余弦值（因为是单位向量不需要除模长）
            float cta = (infX * startX + infY * startY);

            // 设置一个二次衰减因子，让拖尾更长，以点的 index 为计算因子，各次项系数经过多次尝试确定
            int index = pointArray.length - 2 - i;
            float f = (float) (1 / (0.04 * index * index + 0.09 * index + 1));

            // 根据衰减因子计算法向量的模长
            float l = lineWidth * f;

            // 计算法线坐标
            float verticalX = l / cta * startX;
            float verticalY = l / cta * startY;

            // 处理一些宽度太大的点
            if (Math.abs(verticalX) > lineWidth) {
                verticalX = verticalX > 0 ? lineWidth : -lineWidth;
            }
            if (Math.abs(verticalY) > lineWidth) {
                verticalY = verticalY > 0 ? lineWidth : -lineWidth;
            }
            upPoints.add(new PointF(x1 + verticalX, y1 + verticalY));
            downPoints.add(new PointF(x1 - verticalX, y1 - verticalY));
            x0 = x1;
            y0 = y1;
        }
        drawingPath.reset();
        drawingPath.moveTo(upPoints.get(0).x, upPoints.get(0).y);
        for (int i = 1; i < upPoints.size(); i++) {
            drawingPath.lineTo(upPoints.get(i).x, upPoints.get(i).y);
        }
        drawingPath.lineTo(pointArray[0].x, pointArray[0].y);
        Collections.reverse(downPoints);
        for (int i = 0; i < downPoints.size(); i++) {
            drawingPath.lineTo(downPoints.get(i).x, downPoints.get(i).y);
        }
        drawingPath.close();
        canvas.drawPath(drawingPath, strokePaint);
        canvas.drawPath(drawingPath, fillPaint);
    }

    /**
     * 是否点击在红包区域
     */
    private void checkInRect() {
        if (onClickRedPacketListener == null) {
            return;
        }
        for(PointF pointF : pointFList){
            for(MoveModel moveModel : moveList){
                Rect rect = new Rect((int) moveModel.x, (int) moveModel.y, (int) moveModel.x + bitmapWidth, (int) moveModel.y + bitmapHeight);
                if (!moveModel.isRob && rect.contains((int) pointF.x, (int) pointF.y)) {
                    onClickRedPacketListener.onClick(moveModel);
                    return;
                }
            }
        }
    }

    private void generateModel() {
        Random random = new Random();
        MoveModel moveModel = new MoveModel();
        moveModel.moveId = redPacketId;
        moveModel.x = random.nextInt(11) * ((width - bitmapWidth) / 10.f);
        //-bitmapHeight ~ -bitmapHeight-ScreenHeight
        moveModel.initY = moveModel.y = -bitmapHeight - random.nextInt(height);
        moveModel.createTime = System.currentTimeMillis();
        moveList.add(moveModel);
        redPacketId ++;
    }

    public void setRobEnable(boolean robEnable) {
        this.isRobEnable = robEnable;
    }

    public interface OnClickRedPacketListener {

        void onClick(MoveModel moveModel);
    }

    public void destroy() {
        if (null != drawHandler) {
            drawHandler.removeCallbacksAndMessages(null);
            drawHandler.getLooper().quit();
        }

        moveList.clear();
        if (bitmap != null) {
            bitmap.recycle();
            bitmap = null;
        }
        if (bitmapOpen != null) {
            for (int i = 0; i < bitmapOpen.length; i++) {
                bitmapOpen[i].recycle();
                bitmapOpen[i] = null;
            }
            bitmapOpen = null;
        }
    }

    static class RedPointF extends PointF {
        public long timeStamp;

        public RedPointF(float x, float y) {
            super(x, y);
            this.timeStamp = System.currentTimeMillis();
        }
    }
}
